using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using HappyFrogXna.Roles;
using HappyFrogXna.ScreenSystem;
using FarseerPhysics.Factories;
using FarseerPhysics.Dynamics;
using FarseerPhysics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
#if WINDOWS_PHONE
using Microsoft.Xna.Framework.Input.Touch;
#endif
using Microsoft.Xna.Framework.Media;
using HappyFrogXna.DrawingSystem;
using System.Diagnostics;
using HappyFrogXna.Miscellaneous;
using HappyFrogXna.Level;
using HappyFrogXna.ParticleSystem;
using FarseerPhysics.Dynamics.Contacts;

namespace HappyFrogXna
{
    /// <summary>
    /// handle all game logic, like frogs, objects and their interactions.
    /// </summary>
    class GameLogicController
    {
        private Camera2D _camera;
        private float _cameraScaleRadio = 1.0f;

        private World _world;
        private EntityAssemble _entities = new EntityAssemble();

        private ILevel _currentLevel;

        private bool _readyToShoot = false;
        private bool _isLaunched = false;
        private bool _isTracking = false;
        private bool _hasNewAnimalHitDown = false;

        private Vector2 _launchPos = new Vector2(22, 19);

        private Texture2D _blank;
        private Texture2D _belt;
        private Texture2D _frogCase;
          
#if DEBUG
        private Texture2D _debugQuickNextFrogTexture;
#endif

        public GameLogicController(World w, Camera2D c)
        {
            _world = w;
            _camera = c;
        }

        internal void LoadContent()
        {
            _currentLevel = LevelCreator.CreateLevel("GameLevel1", _world);
            _currentLevel.EnterLevel(out _entities);
            _currentLevel.ResetFrogPositong(_entities.Frogs);

            _blank = new Texture2D(GolbalXnaGadgetProvider.Instance.GraphicsDevice, 1, 1);
            _blank.SetData(new[] { Color.White });

            _belt = GolbalXnaGadgetProvider.Instance.Content.Load<Texture2D>(@"Images\belt");
            _frogCase = GolbalXnaGadgetProvider.Instance.Content.Load<Texture2D>(@"Images\case");

#if DEBUG
            _debugQuickNextFrogTexture = GolbalXnaGadgetProvider.Instance.Content.Load<Texture2D>(@"Images\quickNext");
#endif

            _camera.MinPosition = new Vector2(ScreenInfo.ScreenSize.Width / 2, ScreenInfo.ScreenSize.Height / 2);
            _camera.MaxPosition = new Vector2(_currentLevel.Scene.GroundWidthInDisplay - ScreenInfo.ScreenSize.Width / 2, ScreenInfo.ScreenSize.Height / 2);
            _camera.Position = _camera.MinPosition;
            _camera.Jump2Target();

            _world.ContactManager.PostSolve += PostSolve; // handle breakable entities
        }

        private void PostSolve(Contact contact, ContactConstraint impulse)
        {
            if (!_isLaunched)
                return;

            EntityUserData EntityA = impulse.BodyA.UserData as EntityUserData;
            EntityUserData EntityB = impulse.BodyB.UserData as EntityUserData;

            float maxImpulse = 0.0f;
            int count = impulse.PointCount;
            for (int i = 0; i < count; ++i)
            {
                maxImpulse = Math.Max(maxImpulse, impulse.Points[i].NormalImpulse);
            }

            if (EntityA != null)
            {
                if (impulse.BodyA.LinearVelocity.Length() > 1f &&
                (EntityA.Type == EntityType.Ice ||
                EntityA.Type == EntityType.Wood ||
                EntityB.Type == EntityType.Animal)) // hit dynamic objects or animals
                {
                    EntityA.HealthPoint -= maxImpulse;
                    if (EntityA.HealthPoint < 0)
                        EntityA.Killed = true;
                }
                else if (EntityB != null && 
                    impulse.BodyB.LinearVelocity.Length() > 1f && 
                    EntityA.Type == EntityType.Stone) // hit static objects
                {
                    EntityA.HealthPoint -= maxImpulse;
                    if (EntityA.HealthPoint < 0)
                        EntityA.Killed = true;
                }
            }

            if (EntityB != null)
            {
                if (impulse.BodyB.LinearVelocity.Length() > 1f &&
                (EntityB.Type == EntityType.Ice ||
                EntityB.Type == EntityType.Wood ||
                EntityB.Type == EntityType.Animal)) // hit dynamic objects or animals
                {
                    EntityB.HealthPoint -= maxImpulse;
                    if (EntityB.HealthPoint < 0)
                        EntityB.Killed = true;
                }
                else if (EntityA != null &&
                    impulse.BodyA.LinearVelocity.Length() > 1f &&
                    EntityB.Type == EntityType.Stone) // hit static objects
                {
                    EntityB.HealthPoint -= maxImpulse;
                    if (EntityB.HealthPoint < 0)
                        EntityB.Killed = true;
                }
            }
        }

        internal bool HandleInput(ScreenSystem.InputHelper input, Microsoft.Xna.Framework.GameTime gameTime)
        {
            bool handled = _isTracking;

            Vector2 position = _camera.ConvertScreenToWorld(input.Cursor);

            if (input.IsNewMouseButtonPress(MouseButtons.LeftButton)) // mouse press
            {
                Debug.WriteLine("## pos: " + position + " cameraX: " + ConvertUnits.ToSimUnits(_camera.Position.X) + " dubugX: " + (ConvertUnits.ToSimUnits(ScreenInfo.ScreenSize.Width) - 2) + " dubugY: " + (ConvertUnits.ToSimUnits(ScreenInfo.ScreenSize.Height) - 2));
#if DEBUG
                // for debug use, quick turn to next frog
                if (_isLaunched &&
                    position.X - ConvertUnits.ToSimUnits(_camera.Position.X - ScreenInfo.ScreenSize.Width / 2) > ConvertUnits.ToSimUnits(ScreenInfo.ScreenSize.Width) - 2 &&
                    position.Y > ConvertUnits.ToSimUnits(ScreenInfo.ScreenSize.Height) - 2)
                {
                    Debug.WriteLine("## reset frog");
                    _isLaunched = false;
                    _isTracking = false;
                    _camera.EnablePositionTracking = false;
                    _camera.TrackingBody = null;
                    NextFrog();
                    resetCamera();
                    _camera.Position = _camera.MinPosition;
                }
#endif

                if (!_readyToShoot && !_isLaunched)
                {
                    bool touchFrog = _entities.Frogs[0].IsTouched(position, 8);
                    if (touchFrog)
                    {
                        _readyToShoot = true;
                        _launchPos = position;
                        handled = true;
                    }
                }
                else if (_isLaunched && _isTracking)
                {
                    _isTracking = false;
                    _camera.EnablePositionTracking = false;
                    _camera.TrackingBody = null;
                    handled = false;
                }
            }
            else if (input.IsNewMouseButtonRelease(MouseButtons.LeftButton)) // mouse release
            {
                if (_readyToShoot)
                {
                    HFGameTimer timer = GameTimerCreator.CreateTimer();
                    timer.SingleShot(300, () => 
                    {
                        _isLaunched = true;
                        _isTracking = true;
                    });
                    _readyToShoot = false;
                    _entities.Frogs[0].IsActive = true;
                    _entities.Frogs[0].Position = _launchPos;
                    _entities.Frogs[0].Body.ApplyLinearImpulse(10f * (_currentLevel.Scene.SlingshotCenterPos - _launchPos));
                    _camera.EnablePositionTracking = true;
                    _camera.TrackingBody = _entities.Frogs[0].Body;
                }
            }
            else // mouse moving
            {
                if (_readyToShoot)
                {
                    float slingShotRadius = 5;
                    if (position.X < _currentLevel.Scene.SlingshotCenterPos.X)
                    {
                        float angle = (float)Math.Atan2(_currentLevel.Scene.SlingshotCenterPos.Y - position.Y, _currentLevel.Scene.SlingshotCenterPos.X - position.X);
                        _entities.Frogs[0].Rotation = angle;
                        if (GameMathHelper.DistanceOfPos(position, _currentLevel.Scene.SlingshotCenterPos) < slingShotRadius)
                        {
                            _launchPos = position;
                        }
                        else
                        {
                            _launchPos = GameMathHelper.CrossPositionOfACircleAndALinePassCircleCenter(_currentLevel.Scene.SlingshotCenterPos, slingShotRadius, position);
                        }
                    }
                    else
                    {
                        _launchPos.X = _currentLevel.Scene.SlingshotCenterPos.X;
                        _launchPos.Y = position.Y > (_currentLevel.Scene.SlingshotCenterPos.Y + slingShotRadius) ?
                            (_currentLevel.Scene.SlingshotCenterPos.Y + slingShotRadius) :
                            position.Y < (_currentLevel.Scene.SlingshotCenterPos.Y - slingShotRadius) ? (_currentLevel.Scene.SlingshotCenterPos.Y - slingShotRadius) : position.Y;
                    }
                    handled = true;
                }
            }
            return handled;
        }

        internal void Update(Microsoft.Xna.Framework.GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen)
        {
            GameTimerCreator.Update(gameTime);

            _entities.Update(gameTime);
            ParticleCreator.Instance.Update(gameTime.ElapsedGameTime.Milliseconds);

            if (_isLaunched)
            {
                if (_isTracking) // track frog's height
                {
                    float heightOfFrog = (ScreenInfo.ScreenSize.Height - ConvertUnits.ToDisplayUnits(_entities.Frogs[0].Position.Y)) * 1.1f;
                    _cameraScaleRadio = (ScreenInfo.ScreenSize.Height / heightOfFrog < 1) ? ScreenInfo.ScreenSize.Height / heightOfFrog : 1;
                    _camera.Zoom = _cameraScaleRadio;
                    _camera.MinPosition = new Vector2(ScreenInfo.ScreenSize.Width * 0.5f / _cameraScaleRadio, ScreenInfo.ScreenSize.Height * (0.5f - (1 - _cameraScaleRadio)) / _cameraScaleRadio);
                    _camera.MaxPosition = new Vector2(_currentLevel.Scene.GroundWidthInDisplay - ScreenInfo.ScreenSize.Width * 0.5f / _cameraScaleRadio, ScreenInfo.ScreenSize.Height * (0.5f - (1 - _cameraScaleRadio)) / _cameraScaleRadio);
                    _camera.Jump2Target();
                }
                else if (_cameraScaleRadio < 1) // if no tracking but camera scale < 1.0, set it to 1.0
                {
                    resetCamera();
                    _camera.Position = new Vector2(_camera.Position.X, ScreenInfo.ScreenSize.Height / 2);
                    //Camera.Jump2Target();
                }

                _entities.RemoveBrokenObjects();

                for (int i = 0; i < _entities.Animals.Count; ++i)
                {
                    if (_entities.Animals[i].IsVisible && (_entities.Animals[i] as IAnimal).TestBeenHitDown())
                    {
                        if (!(_entities.Animals[i] as IAnimal).HasBeenHitDown)
                        {
                            _hasNewAnimalHitDown = true;
                            (_entities.Animals[i] as IAnimal).HasBeenHitDown = true;
                            //HFGameTimer removeAnimalTimer = GameTimerCreator.CreateTimer();
                            //removeAnimalTimer.SingleShot(1000, _entities.Animals[i].RemoveMe);
                            _entities.Animals[i].RemoveMe();
                        }
                    }
                }

                if (!_entities.TestEntityMoving(ConvertUnits.ToSimUnits(_currentLevel.Scene.GroundWidthInDisplay)))
                {
                    _isLaunched = false;
                    _isTracking = false;
                    _camera.EnablePositionTracking = false;
                    _camera.TrackingBody = null;
                    NextFrog();
                    if (_cameraScaleRadio < 1)
                    {
                        resetCamera();
                        _camera.Position = _camera.MaxPosition;
                        //Camera.Jump2Target();
                    }
                    HFGameTimer timer = GameTimerCreator.CreateTimer();
                    int interval = 1000;
                    if (_hasNewAnimalHitDown)
                    {
                        _hasNewAnimalHitDown = false;
                        interval = 1500;
                    }
                    timer.SingleShot(interval, () =>
                    {
                        _camera.Position = _camera.ConvertWorldToScreen(new Vector2(_currentLevel.Scene.SlingshotCenterPos.X, ConvertUnits.ToSimUnits(ScreenInfo.ScreenSize.Height / 2)));
                        //Debug.WriteLine("## Time2: " + gameTime.TotalGameTime.Seconds + gameTime.TotalGameTime.Milliseconds / 1000);
                    });
                }
            }
        }

        private void resetCamera()
        {
            _cameraScaleRadio = 1.0f;
            _camera.Zoom = _cameraScaleRadio;
            _camera.MinPosition = new Vector2(ScreenInfo.ScreenSize.Width / 2, ScreenInfo.ScreenSize.Height / 2);
            _camera.MaxPosition = new Vector2(_currentLevel.Scene.GroundWidthInDisplay - ScreenInfo.ScreenSize.Width / 2, ScreenInfo.ScreenSize.Height / 2);
        }

        void DrawBelt(SpriteBatch batch, Vector2 point1, Vector2 point2)
        {
            float angle = (float)Math.Atan2(point2.Y - point1.Y, point2.X - point1.X);
            float length = (point2 - point1).Length();
            batch.Draw(_belt, point1, null, Color.White, angle, new Vector2(0, 5), new Vector2(length, 1), SpriteEffects.None, 0); 
        }

        void DrawLine(SpriteBatch batch, Texture2D blank, Color color, int thickness, Vector2 point1, Vector2 point2) 
        {
            float angle = (float)Math.Atan2(point2.Y - point1.Y, point2.X - point1.X);
            float length = (point2 - point1).Length();
            batch.Draw(blank, point1, null, color, angle, new Vector2(0, 0), new Vector2(length, thickness), SpriteEffects.None, 0); 
        }

        internal void Draw(Microsoft.Xna.Framework.GameTime gameTime)
        {
            GolbalXnaGadgetProvider.Instance.GraphicsDevice.Clear(_currentLevel.Scene.BackgroundColor);

            GolbalXnaGadgetProvider.Instance.SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, null, null, null, null, _camera.View);

            // Draw backgroud
            _currentLevel.Scene.Draw();

            // Draw frogs
            if (_readyToShoot)
            {
                float delta = (_currentLevel.Scene.SlingshotCenterPos.Y - _launchPos.Y) * 0.2f / 10.0f;
                DrawBelt(GolbalXnaGadgetProvider.Instance.SpriteBatch, ConvertUnits.ToDisplayUnits(_currentLevel.Scene.SlingshotRightPos), ConvertUnits.ToDisplayUnits(new Vector2(_launchPos.X, _launchPos.Y - delta)));
                GolbalXnaGadgetProvider.Instance.SpriteBatch.Draw(_entities.Frogs[0].Texture, ConvertUnits.ToDisplayUnits(_launchPos), null,
                       Color.White, _entities.Frogs[0].Rotation, _entities.Frogs[0].EntitySprite.Origin, _entities.Frogs[0].ScaleRadioDisplayOnScreen, SpriteEffects.None, 0.5f);
                DrawBelt(GolbalXnaGadgetProvider.Instance.SpriteBatch, ConvertUnits.ToDisplayUnits(_currentLevel.Scene.SlingshotLeftPos), ConvertUnits.ToDisplayUnits(new Vector2(_launchPos.X - 0.2f, _launchPos.Y + delta)));
                GolbalXnaGadgetProvider.Instance.SpriteBatch.Draw(_frogCase, ConvertUnits.ToDisplayUnits(_launchPos), null, Color.White, _entities.Frogs[0].Rotation,
                    new Vector2(_frogCase.Width, _frogCase.Height / 2), 0.5f, SpriteEffects.None, 0);
            }
            else
            {
                GolbalXnaGadgetProvider.Instance.SpriteBatch.Draw(_entities.Frogs[0].Texture, ConvertUnits.ToDisplayUnits(_entities.Frogs[0].Position), null,
                       Color.White, _entities.Frogs[0].Rotation, _entities.Frogs[0].EntitySprite.Origin, _entities.Frogs[0].ScaleRadioDisplayOnScreen, SpriteEffects.None, 0.5f);
            }
            for (int i = 1; i < _entities.Frogs.Count; ++i)
            {
                if (_entities.Frogs[i].IsVisible)
                {
                    GolbalXnaGadgetProvider.Instance.SpriteBatch.Draw(_entities.Frogs[i].Texture, ConvertUnits.ToDisplayUnits(_entities.Frogs[i].Position), null,
                        Color.White, _entities.Frogs[i].Rotation, _entities.Frogs[i].EntitySprite.Origin, _entities.Frogs[i].ScaleRadioDisplayOnScreen, SpriteEffects.None, 0.5f);
                }
            }

            // Draw objects
            for (int i = 0; i < _entities.Objects.Count; ++i)
            {
                if (_entities.Objects[i].IsVisible)
                {
                    GolbalXnaGadgetProvider.Instance.SpriteBatch.Draw(_entities.Objects[i].Texture, ConvertUnits.ToDisplayUnits(_entities.Objects[i].Position), null,
                        Color.White, _entities.Objects[i].Rotation, _entities.Objects[i].EntitySprite.Origin, _entities.Objects[i].ScaleRadioDisplayOnScreen, SpriteEffects.None, 0.5f);
                }
            }

            // Draw animals
            for (int i = 0; i < _entities.Animals.Count; ++i)
            {
                if (_entities.Animals[i].IsVisible)
                {
                    GolbalXnaGadgetProvider.Instance.SpriteBatch.Draw(_entities.Animals[i].Texture, ConvertUnits.ToDisplayUnits(_entities.Animals[i].Position), null,
                        Color.White, _entities.Animals[i].Rotation, _entities.Animals[i].EntitySprite.Origin, _entities.Animals[i].ScaleRadioDisplayOnScreen, SpriteEffects.None, 0.5f);
                }
            }

            // Draw particles
            ParticleCreator.Instance.Draw();
            
#if DEBUG
            if (_isLaunched)
            {
                float corner = ConvertUnits.ToDisplayUnits(2.0f);
                GolbalXnaGadgetProvider.Instance.SpriteBatch.Draw(_debugQuickNextFrogTexture, ScreenInfo.ScreenSize.ToVector2() + new Vector2(_camera.Position.X - ScreenInfo.ScreenSize.Width / 2 - corner, -corner),
                    null, Color.White, 0, Vector2.Zero, new Vector2(corner / _debugQuickNextFrogTexture.Width, corner / _debugQuickNextFrogTexture.Height),
                    SpriteEffects.None, 0.5f);
            }
#endif

            GolbalXnaGadgetProvider.Instance.SpriteBatch.End();
        }

        /// <summary>
        /// Remove current frog and put next frog on slingshot
        /// </summary>
        void NextFrog()
        {
            // reset frog, when debug we re-use frogs :)
            _entities.Frogs[0].Body.Awake = false;
            // remove frog
            _entities.Frogs[0].RemoveMe();
            Entity swapFrog = _entities.Frogs[0];
            for (int i = 0; i < _entities.Frogs.Count - 1; ++i)
            {
                _entities.Frogs[i] = _entities.Frogs[i + 1];
            }
            _entities.Frogs[_entities.Frogs.Count - 1] = swapFrog;
            _currentLevel.ResetFrogPositong(_entities.Frogs);
        }
    }
}
